package main

import "fmt"

// https://refactoringguru.cn/design-patterns/flyweight

type TreeType struct {
	kind    string
	texture string
}

func NewTreeType(kind, texture string) *TreeType {
	return &TreeType{kind: kind, texture: texture}
}

func (t *TreeType) Draw(x, y int) {
	fmt.Printf("tree(%v) texture(%v) plant at (%v, %v)\n", t.kind, t.texture, x, y)
}

type TreeFactory struct {
	treeTypes map[string]*TreeType
}

func NewTreeFactory() *TreeFactory {
	return &TreeFactory{treeTypes: make(map[string]*TreeType)}
}

func (f *TreeFactory) Get(kind string) *TreeType {
	return f.treeTypes[kind]
}

func (f *TreeFactory) Add(kind, texture string) {
	f.treeTypes[kind] = NewTreeType(kind, texture)
}

type Tree struct {
	x, y     int
	treeType *TreeType
}

func NewTree(x, y int, treeType *TreeType) *Tree {
	return &Tree{x: x, y: y, treeType: treeType}
}

func (t *Tree) Draw() {
	t.treeType.Draw(t.x, t.y)
}

type Forest struct {
	trees   []*Tree
	factory *TreeFactory
}

func NewForest(factory *TreeFactory) *Forest {
	return &Forest{factory: factory}
}

func (f *Forest) Plant(kind string, x, y int) {
	f.trees = append(f.trees,
		NewTree(x, y, f.factory.Get(kind)))
}

func (f *Forest) Draw() {
	for _, tree := range f.trees {
		tree.Draw()
	}
}

func main() {
	factory := NewTreeFactory()
	factory.Add("pine", "ZWVzYwo=")
	factory.Add("poplar", "YWRmago=")

	forest := NewForest(factory)
	forest.Plant("pine", 1, 1)
	forest.Plant("pine", 1, 2)
	forest.Plant("pine", 1, 3)

	forest.Plant("poplar", 2, 1)
	forest.Plant("poplar", 2, 2)
	forest.Plant("poplar", 2, 3)

	forest.Draw()
}
